package serialize

import (
	"fmt"
	"runtime/debug"
	"sync"
	"sync/atomic"

	"gddgame.cc/galaxy/utils/def"
	"github.com/tinylib/msgp/msgp"
)

var (
	slotPool = sync.Pool{}
)

func init() {
	slotPool.New = func() interface{} {
		return &Mps{b: make([]byte, 0, 1024)}
	}
}

func GetSlot() *Mps {
	mps := slotPool.Get().(*Mps)
	mps.alloc = 0
	return mps
}

type UnSerializer interface {
	UnSerialize(data []byte) error
}

type IStruct interface {
	msgp.Encodable
	msgp.Decodable
	msgp.Marshaler
	msgp.Unmarshaler
}

type AutoIStruct interface {
	IStruct
	def.AutoSerializer
}

type Slot interface {
	msgp.Marshaler
	msgp.Unmarshaler
	def.OptimizeSerializer
	Bind(s def.Serialize) Slot
	Set(data interface{}) error
	Bytes() []byte
	Clear()
}

type Mps struct {
	b          []byte
	data       IStruct
	serializer def.Serializer
	base       interface{}
	serialize  def.Serialize

	// 用于未修改数据的直接发送，减少序列化次数
	changed bool
	origin  []byte
	alloc   int32
}

func (p *Mps) Serializer() def.OptimizeSerializer {
	return p
}

func (p *Mps) Marshal() ([]byte, error) {
	var err error
	if p.data != nil {
		p.origin, err = p.data.MarshalMsg(p.b)
	} else if p.serializer != nil {
		p.origin, err = p.serializer.Marshal()
	} else if p.base != nil && p.serialize != nil {
		p.origin, err = p.serialize.Marshal(p.base)
	}
	if err != nil {
		return nil, err
	}
	p.changed = false
	return p.origin, err
}

func (p *Mps) MarshalMsg([]byte) ([]byte, error) {
	return p.Marshal()
}

func (p *Mps) Unmarshal(b []byte) error {
	//return msgpack.Unmarshal(b, p)
	var err error
	if p.data != nil {
		_, err = p.data.UnmarshalMsg(b)
	} else if p.serializer != nil {
		err = p.serializer.Unmarshal(b)
	} else if p.base != nil && p.serialize != nil {
		err = p.serialize.Unmarshal(b, p.base)
	}
	p.changed = false
	p.origin = b
	return err
}

func (p *Mps) UnmarshalMsg(b []byte) ([]byte, error) {
	err := p.Unmarshal(b)
	return b, err
}

func (p *Mps) Bind(s def.Serialize) Slot {
	p.serialize = s
	return p
}

func (p *Mps) Set(data interface{}) error {
	if p.changed {
		_, err := p.Marshal()
		if err != nil {
			fmt.Println("mps set err", err)
		}
	}
	switch b := data.(type) {
	case def.OptimizeSerializer:
		b.Alloc()
		p.serializer = b
	case def.Serializer:
		p.serializer = b
	case IStruct:
		p.data = b
	default:
		p.base = data
	}
	if p.origin != nil && len(p.origin) > 0 {
		return p.Unmarshal(p.origin)
	}
	p.changed = true
	return nil
}

func (p *Mps) Clear() {
	p.data = nil
	if p.serializer != nil {
		if v := p.serializer.(def.OptimizeSerializer); v != nil {
			v.Release()
		}
	}
	p.serializer = nil
	p.base = nil
	p.serialize = nil
	p.origin = nil
	p.changed = false
}

func (p *Mps) Alloc() {
	atomic.AddInt32(&p.alloc, 1)
}
func (p *Mps) Release() {
	if p.alloc > 0 {
		atomic.AddInt32(&p.alloc, -1)
		return
	}
	if p.alloc == -1 {
		debug.PrintStack()
		return
	}
	p.Clear()
	p.alloc = -1
	slotPool.Put(p)
}

func (p *Mps) Copy(o def.OptimizeSerializer) {
	if b, ok := o.(*Mps); ok {
		p.data = b.data
		p.serializer = b.serializer
		p.base = b.base
		p.origin = b.origin
		p.changed = b.changed
	} else {
		o.Alloc()
		_ = p.Set(o)
	}
}

func (p *Mps) Bytes() (b []byte) {
	if p.changed {
		_, err := p.Marshal()
		if err != nil {
			fmt.Println("mps bytes err", err)
		}
	}
	return p.origin
}
